package com.neusoft.aclome.cmp.cloud.server.jcce.service;

import com.jcraft.jsch.*;
import com.neusoft.aclome.cmp.cloud.server.jcce.dto.response.JcceJobDTO;
import com.neusoft.aclome.cmp.cloud.server.jcce.entity.JcceJobEntity;
import com.neusoft.aclome.cmp.cloud.server.jcce.repository.JcceJobMapper;
import com.neusoft.aclome.cmp.cloud.server.jcce.wrapper.JcceJobWrapper;
import com.neusoft.aclome.cmp.cloud.server.pub.cloud.entity.PubVirtualMachineEntity;
import com.neusoft.aclome.cmp.cloud.server.pub.cloud.repository.PubVirtualMachineMapper;
import com.neusoft.aclome.cmp.cloud.server.pub.cloud.util.AESUtil;
import com.neusoft.aclome.cmp.common.model.vo.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.Date;

/**
 * 使用jsch通过ssh连接服务器执行job
 */
@Service
public class JobRunnerService {

    private static final Logger log = LoggerFactory.getLogger(JobRunnerService.class);

    @Value("${secret.privateKey}")
    private String key;

    @Autowired
    private JcceJobMapper jcceJobMapper;

    @Autowired
    private PubVirtualMachineMapper pubVirtualMachineMapper;

    @Autowired
    private JcceJobWrapper jcceJobWrapper;

    public JcceJobEntity run(MultipartFile file, JcceJobDTO dto) {
        // 校验传入字段
        validDTO(dto);
        PubVirtualMachineEntity vm = pubVirtualMachineMapper.selectByPrimaryKey(dto.getVirtualMachineId());
        // 校验虚拟机信息
        validVM(vm);

        // 插入一条job
        JcceJobEntity jcceJobEntity = jcceJobWrapper.toEntityFromDTO(dto, null);
        jcceJobEntity.setCloudPlatformId(vm.getDataCenterId());
        jcceJobEntity.setStartTime(new Date());
        jcceJobEntity.setStatus("CREATING");
        jcceJobMapper.insert(jcceJobEntity);

        Session session = null;
        ChannelExec channel = null;

        // upload file
        uploadFile(file, vm, dto);

        // 执行command
        int pid = runCmd(jcceJobEntity, session, channel, vm);

        // 置状态
        jcceJobEntity.setPid(pid);
        jcceJobEntity.setStatus("RUNNING");
        jcceJobMapper.updateByPrimaryKey(jcceJobEntity);

        close(channel);
        close(session);

        return jcceJobEntity;
    }

    private int runCmd(JcceJobEntity jcceJobEntity, Session session, ChannelExec channel, PubVirtualMachineEntity vm) {
        // 获取ssh Session
        try {
            String cmdStr = buildCommandStr(jcceJobEntity);
            log.info("command:" + cmdStr);

            Thread.sleep(5000);

            session = new JSch().getSession("root", vm.getIp(), 22);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword(AESUtil.decrypt(vm.getPassword(), key));
            session.connect();
            channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(cmdStr);
            channel.setErrStream(System.err);

            channel.connect();

            InputStream in = channel.getInputStream();

            byte[] buffer = new byte[1024];
            int len = in.read(buffer);
            if (len > 0) {
                // 将字节数组转换为字符串
                String output = new String(buffer, 0, len);
                // 去掉末尾的换行符
                output = output.trim();
                // 输出进程的PID
                log.info("PID: " + Integer.valueOf(output));
                return Integer.valueOf(output);
            } else {
                throw new RuntimeException("get pid error,");
            }

        } catch (Exception e) {
            log.error("出现异常",e);
            log.warn("SSH连接服务器异常, vmid: " + vm.getId());
            throw new RuntimeException("SSH连接服务器异常", e);
        }
    }

    // 解压缩
    private void decompression(MultipartFile file, PubVirtualMachineEntity vm, JcceJobDTO dto) {
        StringBuffer cmdStr = new StringBuffer();
        cmdStr.append("cd ");
        cmdStr.append(dto.getWorkDir());

        if (file.getOriginalFilename().endsWith(".tar.gz")) {
            cmdStr.append(" && tar -zxf ");
            cmdStr.append(file.getOriginalFilename());
        } else {
            log.info("original file name:" + file.getOriginalFilename() + ", ignore decompression");
            return;
        }

        try {
            log.info("decompression begin, command: " + cmdStr);
            Session session = new JSch().getSession("root", vm.getIp(), 22);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword(AESUtil.decrypt(vm.getPassword(), key));
            session.connect();
            ChannelExec channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(cmdStr.toString());
            channel.setErrStream(System.err);
            channel.connect();

            close(channel);
            close(session);
        } catch (Exception e) {
            log.error("出现异常",e);
            throw new RuntimeException("decompression error", e);
        }

    }

    private void uploadFile(MultipartFile file, PubVirtualMachineEntity vm, JcceJobDTO dto) {
        try {
            if (file != null) {
                log.info("upload file begin..");
                JSch jSch = new JSch();
                Session session = jSch.getSession("root", vm.getIp(), 22);
                session.setConfig("StrictHostKeyChecking", "no");
                session.setPassword(AESUtil.decrypt(vm.getPassword(), key));
                session.connect();
                ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
                channel.connect();
                if (!dto.getWorkDir().endsWith("/")) {
                    dto.setWorkDir(dto.getWorkDir() + "/");
                }

                String dst = dto.getWorkDir() + file.getOriginalFilename();
                log.info("dst: " + dst);
                channel.put(file.getInputStream(), dst, ChannelSftp.OVERWRITE);
                channel.quit();

                close(channel);
                close(session);

                decompression(file, vm, dto);
            } else {
                log.info("upload file is null");
            }
        } catch (Exception e) {
            log.error("出现异常",e);
            throw new RuntimeException("upload file error", e);
        }
    }

    private String buildCommandStr(JcceJobEntity jcceJobEntity) {
        StringBuffer cmdStr = new StringBuffer();
        cmdStr.append("cd ");
        cmdStr.append(jcceJobEntity.getWorkDir());
        cmdStr.append(" && nohup ");
        cmdStr.append(jcceJobEntity.getCommand());
        cmdStr.append(" > nohup.out 2>&1 & echo $!");
        return cmdStr.toString();
    }

    private void validVM(PubVirtualMachineEntity pubVirtualMachineEntity) {
        if (pubVirtualMachineEntity == null) {
            throw new RuntimeException("查询不到虚拟机信息");
        }
        if ("".equals(pubVirtualMachineEntity.getIp())
                || pubVirtualMachineEntity.getIp() == null) {
            throw new RuntimeException("虚拟机没有找到公网IP");
        }
        if ("".equals(pubVirtualMachineEntity.getPassword())
                || pubVirtualMachineEntity.getPassword() == null) {
            throw new RuntimeException("没有找到虚拟机登录凭证");
        }
        if ("!RUNNING".equals(pubVirtualMachineEntity.getState())) {
            log.warn("vm state: " + pubVirtualMachineEntity.getState());
            throw new RuntimeException("虚拟机状态不为运行中，无法执行job");
        }
        try {
            String pwd = AESUtil.decrypt(pubVirtualMachineEntity.getPassword(), key);
            log.info("pwd:" + pwd);
            if ("".equals(pwd) || pwd == null) {
                throw new RuntimeException("没有找到虚拟机登录凭证");
            }
        } catch (Exception e) {
            log.error("出现异常",e);
            throw new RuntimeException("登陆凭证解析错误");
        }
    }

    private void validDTO(JcceJobDTO dto) {
        if (dto.getVirtualMachineId() == null) {
            throw new RuntimeException("virtualMachineId不可为空");
        }
        if ("".equals(dto.getJobName()) || dto.getJobName() == null) {
            throw new RuntimeException("jobName不可为空");
        }
        if ("".equals(dto.getWorkDir()) || dto.getWorkDir() == null) {
            throw new RuntimeException("workDir不可为空");
        }
        if ("".equals(dto.getCommand()) || dto.getCommand() == null) {
            throw new RuntimeException("command不可为空");
        }
    }

    private void close(Session session) {
        try {
            if (session != null) {
                session.disconnect();
            }
        } catch (Exception e) {
            log.warn("close session error");
        }
    }

    private void close(Channel channel) {
        try {
            if (channel != null) {
                channel.disconnect();
            }
        } catch (Exception e) {
            log.warn("close channel error");
        }
    }

}